Skip to main content

Design First

The Design-First approach (also known as top-down) means you define your API contract first—typically using OpenAPI/Swagger, RAML, or API Blueprint—before writing any backend code. This contract serves as a single source of truth for developers, frontend/backend teams, QA, and clients.

Pros of Design-First Approach

  • Clear contract: Ensures all teams agree on how the API behaves before code is written.
  • Documentation first: You get clean API docs from day one.
  • Stub generation: Can generate boilerplate server code & client SDKs automatically.
  • Validation: Helps validate requests/responses against a defined schema.

Tools You Can Use

  • OpenAPI/Swagger: Specification language for REST APIs.
  • Swagger Editor: Online tool to design API visually and export YAML/JSON.
  • swagger-codegen or openapi-generator: Generate Node.js server stubs.
  • express-openapi-validator: Middleware to enforce schema validation.

Example of Design-First Approach

Create openapi.yaml file

openapi: 3.0.0
info:
title: Design-First API Example
version: 1.0.0

paths:
/users:
get:
summary: Get all users
responses:
"200":
description: A list of users
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/User"

post:
summary: Create a new user
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UserInput"
responses:
"201":
description: User created
content:
application/json:
schema:
$ref: "#/components/schemas/User"

components:
schemas:
User:
type: object
properties:
id:
type: integer
name:
type: string

UserInput:
type: object
properties:
name:
type: string
required:
- name

index.js

const express = require("express");
const YAML = require("yamljs");
const { OpenApiValidator } = require("express-openapi-validator");

const apiSpec = YAML.load("./openapi.yaml");

const app = express();
app.use(express.json());

// Serve the OpenAPI spec
app.use("/spec", (req, res) => res.json(apiSpec));

// Init OpenAPI Validator
new OpenApiValidator({
apiSpec: "./openapi.yaml",
validateRequests: true, // auto validate request bodies
validateResponses: true, // auto validate responses
})
.install(app)
.then(() => {
// Routes go after validator is set up
const userRoutes = require("./routes/users");
app.use("/users", userRoutes);

app.use((err, req, res, next) => {
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});

app.listen(3000, () => {
console.log("API running on http://localhost:3000");
console.log("Docs available at Swagger Editor or Redoc");
});
});

routes/users.js

const express = require("express");
const router = express.Router();

const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
];

router.get("/", (req, res) => {
res.status(200).json(users);
});

router.post("/", (req, res) => {
const { name } = req.body;
const newUser = { id: users.length + 1, name };
users.push(newUser);
res.status(201).json(newUser);
});

module.exports = router;

Differences from Code-First

FeatureDesign-FirstCode-First
Start PointAPI Contract (YAML/JSON)Application Code (routes)
ToolingSwagger Editor, OpenAPI GeneratorSwagger JSDoc, Comment Parsers
Contract AuthoritySpec is the source of truthCode is the source of truth
Ideal forTeam collaboration, SDK generation, QAQuick prototyping, smaller teams